
#include "StrUtil.h"
#include "CFunc.h"
#include "depends/encodenormalize.h"

using namespace x2lib;

char *StrUtil::ToLower(char *str, char *buff)
{
	char *orign = str;
	char *ch = str;
	if (str[0] != 0 && buff != NULL)
	{
		strcpy(buff, str);
		orign = buff;
		ch = buff;
	}
	for (; *ch != 0; ch++)
	{
		if (*ch >= 'A' && *ch <= 'Z')
			*ch = *ch - 'A' + 'a';
	}
	return orign;
}

char *StrUtil::ToUpper(char *str, char *buff)
{
	char *orign = str;
	char *ch = str;
	if (str[0] != 0 && buff != NULL)
	{
		strcpy(buff, str);
		orign = buff;
		ch = buff;
	}
	for (; *ch != 0; ch++)
	{
		if (*ch >= 'a' && *ch <= 'z')
			*ch = *ch - 'a' + 'A';
	}
	return orign;
}

int StrUtil::Compare(const char *str1, const char *str2, bool bIgnoreCase)
{
	if (!str1 && str2)
		return -1;
	else if(str1 && !str2)
		return 1;
	else if(!str1 && !str2)
		return 0;
	char *p1 = (char*)malloc(strlen(str1) + 1);
	char *p2 = (char*)malloc(strlen(str2) + 1);
	strcpy(p1, str1);
	strcpy(p2, str2);
	int iRet = 0;
	if (bIgnoreCase)
	{
		iRet = strcmp(ToLower(p1), ToLower(p2));
	}
	else
	{
		iRet = strcmp(str1, str2);
	}
	free(p1);
	free(p2);
	return iRet;
}

int StrUtil::IP2Int(const char *strIP)
{
	//return ntohl(inet_addr(strIP));
	return inet_addr(strIP);
}

char *StrUtil::IP2Str(int iIP, char *strIP)
{
	struct sockaddr_in addr;
#ifdef __X2LIB_WIN32__
	addr.sin_addr.S_un.S_addr = iIP;
#elif defined __X2LIB_LINUX__
	addr.sin_addr.s_addr = iIP;
#endif
	strcpy(strIP, (char*)inet_ntoa(addr.sin_addr));
	return strIP;
}

char *StrUtil::IP2Str(struct sockaddr_in addr, char *strIP)
{
	strcpy(strIP, (char*)inet_ntoa(addr.sin_addr));
	return strIP;
}

wchar_t *StrUtil::Ansi2Unicode(wchar_t *buff, char const *str, int len)
{
	if (str[0] == 0)
		return L"";

	// 在linux遇到过当前环境字符串非常长，因此使用堆内存
	char* pCurLocale = (char*)calloc(1, 4096);
	strcpy(pCurLocale, setlocale(LC_ALL, NULL));
	setlocale(LC_ALL, "zh_CN.UTF-8"); // en_US.UTF-8
	mbstowcs(buff, str, len);
	setlocale(LC_ALL, pCurLocale);
	free(pCurLocale);
	return buff;
}

char *StrUtil::Unicode2Ansi(char *buff, wchar_t const *wstr, int len)
{
	char* pCurLocale = (char*)calloc(1, 4096);
	strcpy(pCurLocale, setlocale(LC_ALL, NULL));
	setlocale(LC_ALL, "zh_CN.UTF-8"); // en_US.UTF-8
	wcstombs(buff, wstr, len);
	setlocale(LC_ALL, pCurLocale);
	free(pCurLocale);
	return buff;
}

wchar_t *StrUtil::Utf82Unicode(wchar_t *buff, char const *str, int len)
{
	if (str[0] == 0)
		return L"";

	char* pCurLocale = (char*)calloc(1, 4096);
	strcpy(pCurLocale, setlocale(LC_ALL, NULL));
	setlocale(LC_ALL, "zh_CN.UTF-8"); // en_US.UTF-8
	mbstowcs(buff, str, len);
	setlocale(LC_ALL, pCurLocale);
	free(pCurLocale);

	return buff;
}

char *StrUtil::Unicode2Utf8(char *buff, wchar_t const *wstr, int len)
{        	
	if (wstr[0] == 0 && wstr[1] == 0)
	{
		return (char*)"";
	}
	char* pCurLocale = (char*)calloc(1, 4096);
	strcpy(pCurLocale, setlocale(LC_ALL, NULL));
	setlocale(LC_ALL, "zh_CN.UTF-8"); // en_US.UTF-8
    wcstombs(buff, wstr, len);
	setlocale(LC_ALL, pCurLocale);
	free(pCurLocale);
    return buff;
}

char *StrUtil::Utf82Ansi(char *buff, char const *str, int len)
{
	wchar_t *wstr = (wchar_t*)calloc(1, len);
	StrUtil::Utf82Unicode(wstr, str, len);
	char *pRet = StrUtil::Unicode2Ansi(buff, wstr, len);
	free(wstr);
	return pRet;
}

char *StrUtil::Ansi2Utf8(char *buff, char const *str, int len)
{
	wchar_t *wstr = (wchar_t*)calloc(1, len);
	StrUtil::Ansi2Unicode(wstr, str, len);
	char *pRet = StrUtil::Unicode2Utf8(buff, wstr, len);
	free(wstr);
	return pRet;
}

char *StrUtil::Gbk2Utf8(char *buff, char const *str, int len)
{
	unsigned int length = 0;
    encode_normalize_init();
    gbk_to_utf8(str, strlen(str), &buff, &length);
	return buff;
}

char *StrUtil::Utf82Gbk(char *buff, char const *str, int len)
{
	unsigned int length = 0;
    encode_normalize_init();
    utf8_to_gbk(str, strlen(str), &buff, &length);
	return buff;
}

int StrUtil::CalcHash(const char *pStr, bool isUnicode, bool isIgnoreCase)
{
	int hash = 0;
	for (; pStr && *pStr; isUnicode ? pStr += 2 : pStr++)
	{
		// 也可以乘以31、131、1313、13131、131313..  
		hash = hash * 131 + (int)((isIgnoreCase && (*pStr) >= 0x61) ? (*pStr) - 0x20 : (*pStr));
		// 有人说将乘法分解为位运算及加减法可以提高效率，如将上式表达为：hash = hash << 7 + hash << 1 + hash + ch;  
		// 但其实在Intel平台上，CPU内部对二者的处理效率都是差不多的，  
		// 我分别进行了100亿次的上述两种运算，发现二者时间差距基本为0（如果是Debug版，分解成位运算后的耗时还要高1/3）；  
		// 在ARM这类RISC系统上没有测试过，由于ARM内部使用Booth's Algorithm来模拟32位整数乘法运算，它的效率与乘数有关：  
		// 当乘数8-31位都为1或0时，需要1个时钟周期  
		// 当乘数16-31位都为1或0时，需要2个时钟周期  
		// 当乘数24-31位都为1或0时，需要3个时钟周期  
		// 否则，需要4个时钟周期  
		// 因此，虽然我没有实际测试，但是我依然认为二者效率上差别不大       
	}
	return hash;
}

int StrUtil::HexChars2Bytes(const char szChars[], char *pBytes, int nBytes)
{
	char strHex[10] = { 0 };
	unsigned int i = 0;
	for (; i < strlen(szChars) && i < (unsigned int)nBytes; i++)
	{
		sprintf(strHex, "0x%c%c", szChars[i * 2], szChars[i * 2 + 1]);
		pBytes[i] = (char)StrUtil::Str2Int(strHex);
	}
	return i/2;
}

int StrUtil::Bytes2HexChars(char *pBytes, int nBytes, char szChars[], int nChars, char chSep)
{
	char strHex[10] = { 0 };
	int sum = nBytes * 3 - (chSep ? 1 : nBytes);
	if (sum > nChars) return 0;
	for (int i=0; i < nBytes; i++)
	{
		sprintf(szChars, "%s%02x%c", szChars, (uint8_t)pBytes[i], (!chSep || (i == nBytes - 1)) ? 0 : chSep);
	}
	return sum;
}

bool StrUtil::IsChineseHead(const char *str)
{
	//如果字符高位为1且下一字符高位也是1则有中文字符
	if ((str[0] & 0x80) && (str[1] & 0x80))
		return true;
	return false;
}

bool StrUtil::MatchWild(const char *szWild, char *szDst, bool isIgnoreCase)
{
	if (szWild == NULL || szDst == NULL) return false;
	int len1 = strlen(szDst);
	int len2 = strlen(szWild);
	int mark = 0;//用于分段标记,'*'分隔的字符串
	int p1 = 0, p2 = 0;

	while (p1 < len1 && p2 < len2)
	{
		if (szWild[p2] == '?')
		{
			p1++;
			p2++;
			continue;
		}
		if (szWild[p2] == '*')
		{ // 如果当前是*号，则mark前面一部分已经获得匹配，从当前点开始继续下一个块的匹配
			p2++;
			mark = p2;
			continue;
		}

		char cDst[2] = { szDst[p1], 0 };
		char cWild[2] = { szWild[p2], 0 };
		if (isIgnoreCase)
		{
			StrUtil::ToLower(cDst);
			StrUtil::ToLower(cWild);
		}
		if (cDst[0] != cWild[0])
		{
			if (p1 == 0 && p2 == 0)
			{ // 如果是首字符，特殊处理，不相同即匹配失败
				return false;
			}
			/*
			* pattern: ...*bdef*...
			*       ^
			*       mark
			*        ^
			*        p2
			*       ^
			*       new p2
			* str1:.....bdcf...
			*       ^
			*       p1
			*      ^
			*     new p1
			* 如上示意图所示，在比到e和c处不想等
			* p2返回到mark处，
			* p1需要返回到下一个位置。
			* 因为*前已经获得匹配，所以mark打标之前的不需要再比较
			*/
			p1 -= p2 - mark - 1;
			p2 = mark;
			continue;
		}
		// 此处处理相等的情况
		p1++;
		p2++;
	}
	if (p2 == len2)
	{
		if (p1 == len1)
		{ // 两个字符串都结束了，说明模式匹配成功
			return true;
		}
		if (szWild[p2 - 1] == '*')
		{ // str1还没有结束，但pattern的最后一个字符是*，所以匹配成功
			return true;
		}
	}
	while (p2<len2)
	{ // pattern多出的字符只要有一个不是*,匹配失败
		if (szWild[p2] != '*')
			return false;
		p2++;
	}
	return true;
}

char *StrUtil::Replace(const char *in, char *out, unsigned int outlen, const char *src, const char *dst)
{
	char *temp = (char*)malloc(sizeof(char)*(strlen(in) + 1));
	memcpy(temp, in, strlen(in) + 1);
	memset(out, 0, outlen);
	char *p = temp;
	unsigned int len = outlen - 1;
	while ((*p != 0) && (len > 0))
	{
		if (strncmp(p, src, strlen(src)) != 0)
		{
			unsigned int n = strlen(out);
			out[n] = *p;
			out[n + 1] = 0;
			p++;
			len--;
		}
		else
		{
			strcat(out, dst);
			p += strlen(src);
			len -= strlen(dst);
		}
	}
	free(temp);
	return out;
}

// isspace在debug模式会检测c的范围（若是中文则可能引发assert）
static bool is_space(int const c)
{
	return (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f');
}

char* StrUtil::Trim(char* pStr, uint8_t iPos)
{
    if (iPos == 0 || iPos == 1)
    {
        char *p = pStr;
        while (is_space(*p)) { ++p; }
        strcpy(pStr, p);
    }

    if (iPos == 0 || iPos == 2)
    {
        int i = strlen(pStr) - 1;
        while (i >= 0 && is_space(pStr[i])) { --i; };
        pStr[i + 1] = '\0';
    }
    return pStr;
}

std::string& StrUtil::Trim(std::string& str, uint8_t iPos)
{
	int index0 = 0;
	int index1 = str.length() - 1;

	if (iPos == 0 || iPos == 1)
	{
		while (is_space(str[index0])) { ++index0; }
	}

	if (iPos == 0 || iPos == 2)
	{
		while (index1 >= 0 && is_space(str[index1])) { --index1; };
	}

	str = str.substr(index0, index1 - index0 + 1);
	return str;
}

std::string& StrUtil::Formats(std::string& str, const char* pszFmt, ...)
{
	va_list body;
	va_start(body, pszFmt);
	uint32_t nChars = vsnprintf(0, 0, pszFmt, body) + 1;
	if (nChars > str.capacity()) { str.resize(nChars); }
	va_start(body, pszFmt); // gcc平台必须重新初始化body
	vsprintf((char*)str.c_str(), pszFmt, body);
	va_end(body);
	
	return str;
}

#if 0
//const std::string& StrUtil::Format(const char* pszFmt, ...)
//{
//	va_list body;
//	va_start(body, pszFmt);
//	int nChars = _vscprintf(pszFmt, body);
//
//    std::mutex mtx;
//    mtx.lock();
//    static std::string str; // 非线程安全，因此下面使用互斥锁
//	str.resize(nChars + 1);
//	vsprintf((char*)str.c_str(), pszFmt, body);
//    mtx.unlock();
//
//    va_end(body);
//
//	return str; // 此处仍然线程不安全
//}

//std::unique_ptr<std::string> StrUtil::Format(const char* pszFmt, ...)
//{
//    std::unique_ptr<std::string> strptr;
//
//    va_list body;
//    va_start(body, pszFmt);
//    int nChars = _vscprintf(pszFmt, body);
//    strptr->resize(nChars + 1);
//    vsprintf((char*)strptr->c_str(), pszFmt, body);
//    va_end(body);
//
//    return strptr;
//}
#endif

char *StrUtil::Float2Str(float f, char buff[], int buffSize, const char *format)
{
    char temp[MAX_PATH] = { 0 };
    sprintf(temp, (format && format[0]) ? format : "%f", f);
    strncpy(buff, temp, buffSize);
	return buff;
}

char *StrUtil::Int2Str(int i, char buff[], int buffSize, const char *format)
{
    char temp[MAX_PATH] = {0};
    sprintf(temp, (format && format[0]) ?format:"%d", i);
	strncpy(buff, temp, buffSize);
	return buff;
}

int StrUtil::Str2Int(const char *iStr)
{
	int iRet = 0;
#if 1
	if (strncmp(iStr, "0x", 2) == 0 || strncmp(iStr, "0X", 2) == 0)
		sscanf((const char *)iStr, "%x", &iRet);
	else
		sscanf((const char *)iStr, "%d", &iRet);
#else
	iRet = _ttoi(iStr);
#endif
	return iRet;
}

float StrUtil::Str2Float(const char *fStr)
{
	return (float)atof(fStr);
}

int StrUtil::Split(const char* str, const char* div, char* pStrs[])
{
	if (!div || !div[0]) { return -1; }

	// 计算所需最小内存
	auto getBytes = [&](int* pnStrs)->int {
		*pnStrs = 0;
		int nBytes = 0;
		char* p0 = (char*)str;
		for (char* p1 = strstr(p0, div); p1; p1 = strstr(p1 + 1, div))
		{
			++(*pnStrs);
			nBytes += (p1 - p0);
			nBytes += sizeof(char*);
			p0 = p1;
		}
		nBytes += strlen(p0) + 1 + sizeof(char*);
		++(*pnStrs);
		return nBytes;
	};

	int nStrs = 0;
	int nBytes = getBytes(&nStrs);

	if (!pStrs) { return nBytes; }

	// 函数要求pStrs必须满足大于等于nBytes，否则以下逻辑将导致内存溢出
	char* pBytes = (char*)pStrs;
	memset(pBytes, 0, nBytes);

	int iStrs = nStrs * sizeof(char*); // 第一个子串的地址索引
	pStrs[0] = pBytes + iStrs;
	strcpy(pStrs[0], str);

	int len = strlen(str);
	int divlen = strlen(div);
	for (int i = nBytes, j = nBytes + 1, k = nStrs; i > iStrs && k > 1; --i) // k==1的子串已经在初始化时设置好
	{ // i记录当前位置，j记录上个子串起始位置，l记录当前子串长度，k记录当前子串索引
		if (memcmp(&pBytes[i], div, divlen) == 0)
		{
			int l = j - i - divlen - 1;
			j = j - (l + 1); // 重计算为当前子串即将存储的位置
			pStrs[--k] = (char*)memmove(&pBytes[j], &pBytes[i + divlen], l);
			memset(&pBytes[i], 0, j - i);
		}
	}

	return nStrs;
}

int StrUtil::Split(const char* str, const char* div, std::vector<std::string>& vecField)
{
	vecField.clear();
	if (!div || !div[0]) { return -1; }

	int len = strlen(str) + 1;
	char* pCache = (char*)calloc(1, len);
	memcpy(pCache, str, len);

	char* p0 = (char*)pCache;
	for (char* p1 = strstr(p0, div); p1; p1 = strstr(p0, div))
	{
		*p1 = 0;
		vecField.emplace_back(p0);
		p0 = p1 + 1;
	}
	vecField.emplace_back(p0);
	free(pCache);

	return vecField.size();
}

int StrUtil::Split(char* str, const char* div, std::vector<std::string>& vecField)
{
	if (!div || !div[0]) { return -1; }

	char* p0 = (char*)str;
	for (char* p1 = strstr(p0, div); p1; p1 = strstr(p0, div))
	{
		char c = *p1;
		*p1 = 0;
		vecField.emplace_back(p0);
		*p1 = c;
		p0 = p1 + 1;
	}
	vecField.emplace_back(p0);

	return vecField.size();
}
